﻿using Framework.Models;
using Framework.Models.Entities;
using Framework.Utility.Extensions;
using Framework.Utility.Helper;
using Newtonsoft.Json.Linq;
using System.Text; 

namespace Framework.Code.FlowWork;

public class FlowRuntime
{
    /// <summary>
    /// 构造函数
    /// </summary>
    public FlowRuntime(Fw_Flowinstance instance)
    {
        dynamic schemeContentJson = instance.SchemeContent.ToJson();//获取工作流模板内容的json对象;
        InitNodes(schemeContentJson);
        flowInstanceId = instance.Id;
        applyUserId = instance.ApplyUserId;
        applyUserName= instance.ApplyUserName;
        currentNodeId = string.IsNullOrWhiteSpace(instance.ActivityId) ? startNodeId.ParseToString() : instance.ActivityId;
        nodeName = currentNode?.nodeName?.ParseToString();
        currentNodeType = (FlowNodeType)currentNode.nodeType;
        counterSign = currentNodeType == FlowNodeType.Approval ? currentNode.properties.counterSign.ParseToBool() : false;
        previousId = instance.PreviousId;
        FrmData = instance.FrmData;
        confluenceOk = instance.ConfluenceOk.ParseToInt();
        confluenceNo = instance.ConfluenceNo.ParseToInt();
        thirdPartyUrl = instance.ThirdPartyUrl;
        //nodeTo为空 没有下一步
        if (!currentNode.nodeTo.Any())
        {
            nextNodeId = Define.FlowEndCode;
            nextNode = null;
        }
        else
        {
            nextNodeId = GetNextApprovalNodeId(currentNodeId);
            nextNode = Nodes[nextNodeId];
        }
    }
     
    #region 获取下一个审批节点

    /// <summary>
    ///  获取下一个审批节点对象
    /// </summary>
    /// <param name="current_nodeId">当前nodeId</param>
    /// <returns>FlowNode</returns>
    public FlowNode GetNextApprovalNode(string current_nodeId = null)
    {
        string nodeId = GetNextApprovalNodeId(current_nodeId);
        return GetFlowNodeById(nodeId);
    }
    /// <summary>
    ///  获取下一个审批节点nodeId
    /// </summary>
    /// <param name="current_nodeId">当前nodeId</param>
    /// <returns>id</returns>
    public string GetNextApprovalNodeId(string current_nodeId = null)
    {
        var current_nextNodeId = GetNextNodeId(current_nodeId);
        if (string.IsNullOrWhiteSpace(current_nextNodeId)) { return string.Empty; }

        var nextNode = Nodes[current_nextNodeId];
        string _nextNodeId = RecurrenceNextApprovalNodeId(nextNode?.nodeId);
        return _nextNodeId;
    }
    #endregion

    /// <summary>
    /// 检查节点会签审核状态
    /// </summary>
    /// <param name="currentNodeId">会签时，currentNodeId是会签开始节点。这个表示当前正在处理的节点</param>
    /// <param name="tag"></param>
    /// <returns>0 会签开始,1 会签结束</returns>
    public int NodeConfluence(string currentNodeId, OptionResultTag tag)
    {
        var forkNode = Nodes[currentNodeId];  //会签节点
        int forkNumber = forkNode.properties.approvers.Count;   //直接与会签节点连接的点，即会签分支数目
        if (tag.Taged == (int)TagState.Ok)
        {
            confluenceOk++;
        }
        else if (tag.Taged == (int)TagState.No)
        {
            return FlowNodeStatus.Join;
        }
        else if (tag.Taged == (int)TagState.Reject)
        {
            return FlowNodeStatus.Join;
        }

        if (confluenceOk == forkNumber)
        {
            confluenceOk = 0;//重置会签次数，为下个节点审批做准备
            confluenceNo = 0;//重置会签次数，为下个节点审批做准备
            return FlowNodeStatus.Join;
        }
        return FlowNodeStatus.Fork;
    }

    #region 获取节点信息

    /// <summary>
    /// 获取节点类型 1 开始节点,2 网关节点, 3 条件节点,4 审批节点 5 结束节点 -1 不可用节点
    /// </summary>
    /// <param name="nodeId"></param>
    /// <returns></returns>
    public FlowNodeType GetNodeType(string nodeId)
    {
        switch (Nodes[nodeId].nodeType)
        {
            //开始
            case 1:
                return FlowNodeType.Start;
            //网关节点
            case 2:
                return FlowNodeType.Getway;
            //条件节点
            case 3:
                return FlowNodeType.Condition;
            //审批人节点
            case 4:
                return FlowNodeType.Approval;
            //结束
            case 5:
                return FlowNodeType.End;
            //不可用
            default:
                return FlowNodeType.Disable;
        }
    }
     
    /// <summary>
    /// 获取节点信息
    /// </summary>
    /// <remarks>根据节点id获取节点对象</remarks>
    /// <param name="nodeId">节点id</param>
    /// <returns></returns>
    public FlowNode GetFlowNodeById(string nodeId)
    {
        if (string.IsNullOrWhiteSpace(nodeId) || nodeId == Define.FlowEndCode) { return null; }
        return Nodes[nodeId];
    }

    /// <summary>
    /// 获取上一个节点nodeId
    /// </summary>
    /// <remarks>根据节点id获取前一节点id</remarks>
    /// <param name="nodeId">节点id</param>
    /// <returns></returns>
    /// <exception cref="ArgumentNullException"></exception>
    public string GetPreNodeId(string nodeId = null)
    {
        var currentNode = nodeId == null ? Nodes[startNodeId] : Nodes[nodeId];
        if (currentNode == null)
        {
            throw new ArgumentNullException("无法找到上一个点");
        }
        return currentNode.nodeFrom;
    }

    /// <summary>
    /// 获取上一个节点对象
    /// </summary>
    /// <remarks>根据节点id获取前一节点对象</remarks>
    /// <param name="nodeId"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public FlowNode GetPreNode(string nodeId = null)
    {
        var currentNode = nodeId == null ? Nodes[startNodeId] : Nodes[nodeId];
        if (currentNode == null)
        {
            throw new ArgumentNullException("无法找到上一个点");
        }
        return Nodes[currentNode.nodeFrom];
    }

    /// <summary>
    /// 获取下一个节点对象
    /// </summary>
    /// <remarks>根据节点id获取下一节点对象</remarks>
    /// <remarks>不适用getway节点</remarks>
    /// <param name="nodeId">节点id</param>
    /// <returns></returns>
    public FlowNode GetNextNode(string nodeId = null)
    {
        string nextNodeId = GetNextNodeId(nodeId); 
        return string.IsNullOrWhiteSpace(nextNodeId) ? null : Nodes[nextNodeId];
    }
    #endregion

    #region 消息通知

    /// <summary>
    /// 通知三方系统，节点执行情况
    /// </summary>
    public void NotifyThirdParty(HttpClient client, OptionResultTag tag)
    {
        var postData = new
        {
            flowInstanceId,
            nodeName,
            nodeId = currentNodeId,
            userId = tag.UserId,
            userName = tag.UserName,
            result = tag.Taged, //1：通过;2：不通过；3驳回
            description = tag.Description,
            execTime = tag.TagedTime,
            isFinish = currentNodeId != Define.FlowEndCode ? FlowInstanceStatus.Running : FlowInstanceStatus.Finished,
        };

        using (HttpContent httpContent = new StringContent(postData.ToJson(), Encoding.UTF8))
        {
            httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
            client.PostAsync(this.thirdPartyUrl, httpContent);
        }
    }

    #endregion 
    
    #region 私有方法

    /// <summary>
    /// 获取工作流节点的字典列表:key节点id
    /// </summary>
    /// <param name="schemeContentJson"></param>
    /// <returns></returns>
    private void InitNodes(dynamic schemeContentJson)
    {
        Nodes = new Dictionary<string, FlowNode>
        {
            { 
                Define.FlowEndCode, GetEndFlowNode()
            }
        };
        foreach (JObject item in schemeContentJson)
        {
            var node = item.ToObject<FlowNode>(); 
            if (!Nodes.ContainsKey(node.nodeId))
            {
                Nodes.Add(node.nodeId, node);
            }
            if (node.nodeType == (int)FlowNodeType.Start)
            {
                this.startNodeId = node.nodeId;
            }
        }
    }

    /// <summary>
    /// 获取下一个节点 
    /// </summary>
    /// <remarks>不适用gatway节点</remarks>
    /// <returns>nodeId</returns>
    private string GetNextNodeId(string nodeId)
    {
        if (string.IsNullOrWhiteSpace(nodeId)) { return string.Empty; }
        if (Nodes[nodeId] == null) { return string.Empty; }
        return Nodes[nodeId].nodeTo.IsNullOrEmpty() ? string.Empty : Nodes[nodeId].nodeTo.First();
    }

    /// <summary>
    /// 获取Getway节点下的条件节点
    /// </summary>
    private List<string> GetGetwayNextNodeId(string nodeId)
    {
        var result = new List<string>();
        if (nodeId.IsNullOrEmpty()) { return result; }
        if (Nodes[nodeId] == null) { return result; }
        return Nodes[nodeId].nodeTo;
    }
    /// <summary>
    /// 递归查询 Approval节点Id
    /// </summary>
    /// <param name="nextNodeId"></param>
    /// <returns></returns>
    private string RecurrenceNextApprovalNodeId(string nextNodeId)
    {
        var nextNode = Nodes[nextNodeId];
        if (nextNode == null) { return string.Empty; }
        string _nextNodeId = string.Empty;
        //如果下一个节点是 Approval节点 则直接返回 nodeId
        if (nextNode.nodeType == (int)FlowNodeType.Approval)
        {
            return nextNode.nodeId;
        }
        //如果下一个节点是 Getway节点 则判断条件节点
        if (nextNode.nodeType == (int)FlowNodeType.Getway)
        {
            var conditionNodes = Nodes.Where(c => nextNode.nodeTo.Contains(c.Key))?.OrderBy(c => c.Value?.properties?.priority ?? "0");
            if (conditionNodes == null || conditionNodes.Count() == 0) { return string.Empty; }
            foreach (var i_node in conditionNodes)
            {
                if (!IsPassConditionNode(i_node.Value)) { continue; }
                _nextNodeId = GetNextNodeId(i_node.Value.nodeId);
                break;
            }
        }
        //递归查询 主要应对 Getway节点 下还是 Getway节点
        return RecurrenceNextApprovalNodeId(_nextNodeId);
    }

    /// <summary>
    /// 判断条件节点是否通过
    /// </summary>
    private bool IsPassConditionNode(string nodeId)
    {
        var nowNode = Nodes[nodeId];
        if (nowNode == null)
        {
            throw new Exception($"无法寻找到条件节点节点,nodeId = {nodeId}");
        }
        return IsPassConditionNode(nowNode);
    }

    /// <summary>
    /// 判断条件节点是否通过
    /// </summary>
    private bool IsPassConditionNode(FlowNode node)
    {
        bool result = true;
        if (node.nodeType != (int)FlowNodeType.Condition) { return true; }

        var conditions = node.properties.conditions;
        if (conditions.IsNullOrEmpty()) { return true; }
        var conditionsValues = conditions.SelectMany(x => x.conditionValue).ToList();

        FrmData = FrmData.ToLower();//统一转小写
        var frmDataJson = FrmData.ToJObject();//获取数据内容

        foreach (var it in conditionsValues)
        {
            if (!result) return false;
            bool isDecimal = decimal.TryParse(it.value, out decimal value);
            var fieldVal = frmDataJson.Value<string>(it.fieldName.ToLower());
            if (fieldVal.IsNullOrEmpty())
            {
                result = false;
                continue;
            }
            if (isDecimal)  //如果是数字或小数
            {
                decimal frmvalue = fieldVal.ParseToDecimal(); //表单中填写的值
                switch (it.key)
                {
                    case DataCompare.Equal:
                        result &= it.value == fieldVal;
                        break;

                    case DataCompare.Larger:
                        result &= frmvalue > value;
                        break;

                    case DataCompare.Less:
                        result &= frmvalue < value;
                        break;

                    case DataCompare.LargerEqual:
                        result &= frmvalue >= value;
                        break;

                    case DataCompare.LessEqual:
                        result &= frmvalue <= value;
                        break;
                }
            }
            else //如果只是字符串，只判断相等
            {
                result &= it.value == fieldVal;
            }
        }
        return result;
    }

    private FlowNode GetEndFlowNode()
    {
        var node = new FlowNode();
        node.nodeId = "-1";
        node.nodeType = (int)FlowNodeType.End;
        node.nodeName = "流程结束";
        node.nodeDisplayName = "流程结束";
        node.prevId = new List<string>();
        node.nodeFrom = "";
        node.nodeTo = new List<string>();
        node.nodeProperty = 0;
        node.FlowNodeStatus = FlowInstanceStatus.Finished;
        node.properties = null;
        return node;
    }


    #endregion

    #region 用于审批流程查找审批人 备份

    ///// <summary>
    ///// 如果当前节点是Approval 则返回当前节点nodeId 否则 获取下一个审批节点nodeId
    ///// </summary>
    ///// <param name="current_nodeId">当前nodeId</param>
    ///// <returns></returns>
    //public FlowNode GetCurrentOrNextApprovalNode(string current_nodeId = null)
    //{
    //    string nodeId = GetCurrentOrNextApprovalNodeId(current_nodeId);
    //    return GetFlowNodeById(nodeId);
    //}

    ///// <summary>
    ///// 如果当前节点是Approval 则返回当前节点nodeId 否则 获取下一个审批节点nodeId
    ///// </summary>
    ///// <param name="current_nodeId">当前nodeId</param>
    ///// <returns></returns>
    //public string GetCurrentOrNextApprovalNodeId(string current_nodeId = null)
    //{
    //    //nodeId 为空 直接返回
    //    if (current_nodeId.IsNullOrEmpty()) { return string.Empty; }
    //    var current_Node = Nodes[current_nodeId]; 
    //    //如果当前节点是 Approval节点 则直接返回 nodeId，否则向下查找
    //    if (current_Node.nodeType == (int)FlowNodeType.Approval) { return current_Node.nodeId; } 
    //    var nextNode = GetNextNode(current_nodeId); 
    //    string _nextNodeId = RecurrenceNextApprovalNodeId(nextNode?.nodeId); 
    //    //递归查询 主要应对 Getway节点 下还是 Getway节点
    //    return _nextNodeId;
    //}
    #endregion

    #region 属性

    /// <summary>
    /// 运行实例的Id
    /// </summary>
    public string flowInstanceId { get; set; }

    /// <summary>
    /// 流程申请人Id
    /// </summary>
    public string applyUserId { get; set; }
    /// <summary>
    /// 流程申请人
    /// </summary>
    public string applyUserName { get; set; }
    /// <summary>
    /// 开始节点的ID
    /// </summary>
    public string startNodeId { get; set; }

    /// <summary>
    /// 上一个节点Id
    /// </summary>
    public string previousId { get; set; }

    /// <summary>
    /// 节点名称
    /// </summary>
    public string nodeName { get; set; }

    /// <summary>
    /// 当前节点的ID
    /// </summary>
    public string currentNodeId { get; set; }

    /// <summary>
    /// 当前节点类型  1 start,2 getway, 3 condition  4 approval 5 end
    /// </summary>
    public FlowNodeType currentNodeType { get; set; }

    /// <summary>
    /// 当前节点的对象
    /// </summary>
    public FlowNode currentNode => Nodes[currentNodeId];

    /// <summary>
    /// 当前节点类型 0会签 1 或签
    /// </summary>
    public bool counterSign { get; set; }

    /// <summary>
    /// 会签通过的个数
    /// </summary>
    public int confluenceOk { get; set; }

    /// <summary>
    /// 会签拒绝的个数
    /// </summary>
    public int confluenceNo { get; set; }

    /// <summary>
    /// 下一个节点
    /// </summary>
    public string nextNodeId { get; set; }

    /// <summary>
    /// 下一个节点对象
    /// </summary>
    public FlowNode nextNode { get; set; }

    /// <summary>
    /// 实例节点集合
    /// </summary>
    public Dictionary<string, FlowNode> Nodes { get; set; }

    /// <summary>
    /// 表单数据 条件验证
    /// </summary>
    public string FrmData { get; set; }

    /// <summary>
    ///  流程执行时，三方回调的URL地址
    /// </summary>
    public string? thirdPartyUrl { get; set; }

    #endregion 属性

}